/********************************************************************************
 * Copyright(c) 2020-2025 VINCENT_WY All rights reserved.
 * - Filename
 * - Author  Vincent
 * - Version V1.0.0
 * - Date    2023/09/21
 * - Brief
 * - FunctionList:
 ******************************************************************************
 * History:
 *
 *
 *
 ******************************************************************************
 */
#include "DispatchReplayImpl.h"
#include "TimeUtil.h"
#include "FrameWrap.h"
#include "logutil.h"

#include <fstream>
#include <dirent.h>
#include <exception>
#include <filesystem>
#include <string.h>

using namespace std;
using namespace dispatch;

DispatchReplayImpl::DispatchReplayImpl(const int &id, const std::string &path)
    : _cameraId(id), _root(path)
{
  ReadFile();
}

DispatchReplayImpl::~DispatchReplayImpl() {}

void DispatchReplayImpl::start(const std::function<void(std::shared_ptr<CameraFrame> &&)> &callback,
                               const std::function<void(std::shared_ptr<BevFrame> &&)> &cb, const long long int &time)
{
  _startTime = time;
  _replayLoop = std::make_unique<std::thread>(&DispatchReplayImpl::ReplayLoop, this);
  _invokeLoop =
      std::make_unique<std::thread>(&DispatchReplayImpl::InvokeLoop, this, callback, cb);
  _status = Status::START;
}

bool DispatchReplayImpl::isComplete() { return _isComplete; }

void DispatchReplayImpl::submit(std::shared_ptr<CameraFrame> frame)
{
  _fileInfoQueue.push_back(frame);
}

FileMap DispatchReplayImpl::getFile()
{
  return _fileMap;
}

void DispatchReplayImpl::doRepeat() { _status = Status::REPEAT; }
void DispatchReplayImpl::doReplay() { _status = Status::CONTINUE; }

void DispatchReplayImpl::stop()
{
  _status = Status::IDLE;
  _isExit = true;
  _fileInfoQueue.close();
  _frameCv.notify_all();
  if ((_replayLoop != nullptr) && _replayLoop->joinable())
  {
    _replayLoop->join();
  }
  if ((_invokeLoop != nullptr) && _invokeLoop->joinable())
  {
    _invokeLoop->join();
  }

  LOGD("[REPLAY IMPL] destroy ...");
}

void DispatchReplayImpl::ReplayLoop()
{
  int index = 0;
  long long int mLastTimeStamp{0};
  while (!_isExit)
  {
    switch (_status)
    {
    case Status::START:
    {
      index = 0;
      mLastTimeStamp = _startTime;
      _isComplete = false;
      _status = Status::IDLE;
    }
    break;

    case Status::REPEAT:
    {
      _status = Status::START;
    }
    break;

    case Status::CONTINUE:
    {
      auto t1 = TimeUtil::now_us();
      auto frame = std::make_shared<CameraFrame>();
      _fileInfoQueue.pop(frame);
      auto t2 = TimeUtil::now_us();

      long long int mCurrentTimeStamp =
          frame->timestamp.tv_sec * 1000000 + frame->timestamp.tv_usec;
      // 等待两帧之间的时间间隔，时间是us
      std::this_thread::sleep_for(
          std::chrono::microseconds((int)(mCurrentTimeStamp - mLastTimeStamp)));
      {
        std::lock_guard<std::mutex> lock(_frameMtx);
        _frameDqu.push_back(frame);
      }
      _frameCv.notify_one();
      auto t3 = TimeUtil::now_us();

      // printf("id:%d , %lld, %lld, %lld, %lld \n", frame->cameraId,
      //        mLastTimeStamp, mCurrentTimeStamp,
      //        mCurrentTimeStamp - mLastTimeStamp, t3 - t1);

      mLastTimeStamp = mCurrentTimeStamp;
      index++;
      if (index >= (int)_fileMap.size())
      {
        _status = Status::IDLE;
        _isComplete = true;
        index = 0;
      }
    }
    break;

    case Status::IDLE:
    {
      std::this_thread::sleep_for(std::chrono::milliseconds(3));
    }
    break;

    default:
      break;
    }
  }
}

void DispatchReplayImpl::InvokeLoop(
    const std::function<void(std::shared_ptr<CameraFrame> &&)> &callbak, const std::function<void(std::shared_ptr<BevFrame> &&)> &bc)
{
  while (!_isExit)
  {
    std::unique_lock<std::mutex> lock(_frameMtx);
    _frameCv.wait(lock, [this]
                  { return _frameDqu.size() > 0 || _isExit; });
    if (_isExit)
      break;
    auto frame = _frameDqu.front();
    _frameDqu.pop_front();
    lock.unlock();

    if (_cameraId == FrameWrap::DEVICE_TYPE_BEV)
    {
      auto bf = make_shared<BevFrame>();
      bf->width = frame->width;
      bf->height = frame->height;
      bf->timestamp = frame->timestamp;
      bf->size = frame->size;
      bf->format = frame->format;
      bf->buffer = std::move(frame->buffer);
      bc(std::move(bf));
    }
    else
    {
      callbak(std::move(frame));
    }
  }
}

void DispatchReplayImpl::ReadFile()
{
  std::string mRoot = _root;
  if (mRoot.back() != '/')
  {
    mRoot.append("/");
  }
  DIR *pDir;
  struct dirent *ptr;
  if (!(pDir = opendir(mRoot.c_str())))
  {
    LOGE("Folder doesn't Exist!");
    return;
  }

  std::map<int, std::string> fileMap;
  while ((ptr = readdir(pDir)) != 0)
  {
    if (strcmp(ptr->d_name, ".") != 0 && strcmp(ptr->d_name, "..") != 0)
    {
      std::string mFileName = ptr->d_name;
      int strid;
      DispatchFileInfo mFileInfo;
      mFileInfo.type = (DeviceID)_cameraId;
      sscanf(mFileName.c_str(), "%d_%lld_%dx%dx%d.nv12", &mFileInfo.freq, &mFileInfo.timestamp,
             &strid, &mFileInfo.width, &mFileInfo.height);
      mFileInfo.fileName = mRoot + mFileName;
      _fileMap.insert(std::make_pair(mFileInfo.timestamp, mFileInfo));
    }
  }
  closedir(pDir);
}
